/**
 * @file
 *           base64.h
 * @brief
 *          <fillin>purpose
 *
 * @par Author (last changes):
 *          - Herea Adrian
 *          
 * @par Description:
 *
 *
 * @par Module-History:
 * @verbatim
 *  Date        Author				Reason
 *  15.01.2010  Herea Adrian         Initial creation
 *
 * @endverbatim
 *
 * @par Copyright Notice:
 * @verbatim
 * Copyright (C) 2010
 * All Rights Reserved.
 * The reproduction, transmission or use of this document or its contents is
 * not permitted without express written authority.
 * Offenders will be liable for damages. All rights, including rights created
 * by patent grant or registration of a utility model or design, are reserved.
 **/
#include "StdAfx.h"
#include "AbstractSocket.h"

AbstractSocket::AbstractSocket(SOCKET winSocket) :
    m_winSocket(winSocket), m_pListener(NULL)
{
}

int AbstractSocket::protocolSend(string sProtocol)
{
    if (m_winSocket == INVALID_SOCKET)
    {
        return -1;
    }
    sProtocol = MSG_BEGIN_TAG + sProtocol + MSG_END_TAG;
    int iRes = 0;
    int iSend = 0;
    int iToSend = (int) sProtocol.length();
    while (iSend != iToSend)
    {
        iRes = socketSelect();
        if (!iRes)
        {
            break;
        }
        if (iRes < 0)
        {
            iRes = WSAGetLastError();
        }
        iSend += send(m_winSocket, &sProtocol.c_str()[iSend], iToSend - iSend, 0);
    }
    return iRes;
}

int AbstractSocket::socketSelect(int iTimeOut)
{
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(m_winSocket, &fds);
    timeval SendTimeout;
    SendTimeout.tv_sec = 0;
    SendTimeout.tv_usec = iTimeOut;
    return select(0, NULL, &fds, NULL, &SendTimeout);
}

void AbstractSocket::protocolReceive(string sProtocol)
{
    m_sRecvMsgBuffer += sProtocol;
    // first find message begin tag
    while (TRUE)
    {
        int iBegin = (int) m_sRecvMsgBuffer.find(MSG_BEGIN_TAG);
        if (iBegin == string::npos)
        {
            break;
        }
        // then find message end tag
        int iEnd = (int) m_sRecvMsgBuffer.find(MSG_END_TAG);
        if (iEnd == string::npos)
        {
            break;
        }

        if (iBegin < iEnd)
        {
            string sMessage = m_sRecvMsgBuffer.substr(iBegin + 4, iEnd - iBegin
                    - 4);
            m_sRecvMsgBuffer = m_sRecvMsgBuffer.substr(iEnd + 4);
            if (m_pListener != NULL)
            {
                m_pListener->receive(sMessage);
            }
        }
        else
        {
            m_sRecvMsgBuffer = m_sRecvMsgBuffer.substr(iBegin);
        }
    }
}
int AbstractSocket::socketListen()
{
    if (m_winSocket == INVALID_SOCKET)
    {
        return -1;
    }

    char buffer[1024];
    int res = 0;

    while ((res = recv(m_winSocket, buffer, sizeof(buffer) - 1, 0)) > 0)
    {
        if ((res != SOCKET_ERROR)&& (m_pListener != NULL))
        {
            string sProtocol = string(buffer, res);
            protocolReceive(sProtocol);
        }
    }
    if (res == 0)
    {
        // cout<<"Connection closed ... "<<endl;
    }
    else
    {
        string sMessage("");
        switch (WSAGetLastError())
        {
        case WSANOTINITIALISED:
            sMessage
                    = "A successful WSAStartup call must occur before using this function.";
            break;
        case WSAENETDOWN:
            sMessage = "The network subsystem has failed.";
            break;
        case WSAEFAULT:
            sMessage
                    = "The buf parameter is not completely contained in a valid part of the user address space.";
            break;
        case WSAENOTCONN:
            sMessage = "The socket is not connected.";
            break;
        case WSAEINTR:
            sMessage
                    = "The (blocking) call was canceled through WSACancelBlockingCall.";
            break;
        case WSAEINPROGRESS:
            sMessage
                    = "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function.";
            break;
        case WSAENETRESET:
            sMessage
                    = "The connection has been broken due to the keep-alive activity detecting a failure while the operation was in progress.";
            break;
        case WSAENOTSOCK:
            sMessage = "The descriptor is not a socket.";
            break;
        case WSAEOPNOTSUPP:
            sMessage
                    = "MSG_OOB was specified, but the socket is not stream-style such as type SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only send operations.";
            break;
        case WSAESHUTDOWN:
            sMessage
                    = "The socket has been shut down; it is not possible to receive on a socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.";
            break;
        case WSAEWOULDBLOCK:
            sMessage
                    = "The socket is marked as nonblocking and the receive operation would block.";
            break;
        case WSAEMSGSIZE:
            sMessage
                    = "The message was too large to fit into the specified buffer and was truncated.";
            break;
        case WSAEINVAL:
            sMessage
                    = "The socket has not been bound with bind, or an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled or (for byte stream sockets only) len was zero or negative.";
            break;
        case WSAECONNABORTED:
            sMessage
                    = "The virtual circuit was terminated due to a time-out or other failure. The application should close the socket as it is no longer usable.";
            break;
        case WSAETIMEDOUT:
            sMessage
                    = "The connection has been dropped because of a network failure or because the peer system failed to respond.";
            break;
        case WSAECONNRESET:
            sMessage
                    = "The virtual circuit was reset by the remote side executing a hard or abortive close. The application should close the socket as it is no longer usable. On a UPD-datagram socket this error would indicate that a previous send operation resulted in an ICMP Port Unreachable message.";
            break;
        default:
            sMessage = "WinSock error : " + res;
            break;
        }
        //  cout<<sMessage.c_str()<<endl;
    }
    return res;
}

void AbstractSocket::registerListener(ReceiverListener* pListener)
{
    m_pListener = pListener;
}

AbstractSocket::~AbstractSocket(void)
{
    if (m_winSocket != INVALID_SOCKET)
    {
        closesocket(m_winSocket);
    }
}
